using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TripleA.Core.Database;
using TripleA.Core.Database.SqlServer;
using TripleA.Core.Interfaces.Entities;
using TripleA.Core.Interfaces.Exceptions;

namespace TripleA.Core.Extensions.Database
{
    public class SqlServer2008AssertRolesStep<T> : SqlServerCommandStep<T>
        where T : Context
    {
        public string Database { get; set; }
        public string Username { get; set; }
        public string[] Roles { get; set; }

        public override void PreValidate(T context)
        {
            if (string.IsNullOrWhiteSpace(Database))
                throw new StepConfigurationException("Database property not set, null or empty");
            if (string.IsNullOrWhiteSpace(Username))
                throw new StepConfigurationException("Username property not set, null or empty");
            if (Roles == null || Roles.Length == 0)
                throw new StepConfigurationException("Roles property not set or none specified");
        }

        protected virtual ISqlStatement BuildStatement()
        {
            return SqlServerStatement.Create("SELECT user_name(role_principal_id) AS RoleName FROM sys.database_role_members")
                .Append("WHERE user_name(member_principal_id)=")
                .InsertParameter("@pUserName", Username);
        }

        public override void Execute(T context)
        {
            var actualRoles = new List<string>();

            Statement = BuildStatement();
            ExecuteReader(context, (reader, row) =>
                                       {
                                           actualRoles.Add(reader["RoleName"].ToString());
                                           return true;
                                       });

            var roleExceptions = Roles.Where(role => !actualRoles.Contains(role)).ToList();

            if (!roleExceptions.Any())
                return;

            var sb = new StringBuilder(string.Format("User '{0}' is not in role(s): ", Username));
            roleExceptions.ToList().ForEach(rx => sb.AppendFormat("{0},", rx));
            var msg = sb.ToString().TrimEnd(',');
            
            throw new ApplicationException(msg);
        }
    }
}